package com.yj.web.WeChat.api;

import com.yj.web.WeChat.Message.WxXmlMessage;
import com.yj.web.WeChat.Message.WxXmlOutMessage;
import com.yj.web.util.LogExceptionHandler;
import com.yj.web.util.Session.InternalSession;
import com.yj.web.util.Session.InternalSessionManager;
import com.yj.web.util.Session.StandardSessionManager;
import com.yj.web.util.Session.WxSessionManager;
import com.yj.web.util.WxErrorExceptionHandler;
import com.yj.web.util.WxMessageDuplicateChecker;
import com.yj.web.util.WxMessageInMemoryDuplicateChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Created by yanlinyang on 2017/9/10.
 */
public class WxMessageRouter {
    protected final Logger log = LoggerFactory.getLogger(WxMessageRouter.class);
    private static final int DEFAULT_THREAD_POOL_SIZE = 100;

    private final List<WxMessageRouterRule> rules = new ArrayList<WxMessageRouterRule>();
    private final WxService wxService;
    private ExecutorService executorService;
    private WxMessageDuplicateChecker messageDuplicateChecker;
    private WxSessionManager sessionManager;
    private WxErrorExceptionHandler exceptionHandler;

    public WxMessageRouter(WxService wxService) {
        this.wxService = wxService;
        this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
        this.messageDuplicateChecker = new WxMessageInMemoryDuplicateChecker();
        this.sessionManager = new StandardSessionManager();
        this.exceptionHandler = new LogExceptionHandler();
    }

    public void setExecutorService(ExecutorService executorService) {
        this.executorService = executorService;
    }

    public void setMessageDuplicateChecker(WxMessageDuplicateChecker messageDuplicateChecker) {
        this.messageDuplicateChecker = messageDuplicateChecker;
    }

    public void setSessionManager(WxSessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    public void setExceptionHandler(WxErrorExceptionHandler exceptionHandler) {
        this.exceptionHandler = exceptionHandler;
    }

    List<WxMessageRouterRule> getRules() {
        return this.rules;
    }

    public WxMessageRouterRule rule() {
        return new WxMessageRouterRule(this);
    }

    public WxXmlOutMessage route(final WxXmlMessage wxMessage) {
//        if (isDuplicateMessage(wxMessage)) {
//            // 如果是重复消息，那么就不做处理
//            return null;
//        }

        final List<WxMessageRouterRule> matchRules = new ArrayList<WxMessageRouterRule>();
        // 收集匹配的规则
        for (final WxMessageRouterRule rule : rules) {
            if (rule.test(wxMessage)) {
                matchRules.add(rule);
                if(!rule.isReEnter()) {
                    break;
                }
            }
        }

        if (matchRules.size() == 0) {
            return null;
        }

        WxXmlOutMessage res = null;
        final List<Future> futures = new ArrayList<Future>();
        for (final WxMessageRouterRule rule : matchRules) {
            // 返回最后一个非异步的rule的执行结果
            if(rule.isAsync()) {
                futures.add(
                        executorService.submit(new Runnable() {
                            public void run() {
                                rule.service(wxMessage, wxService, sessionManager, exceptionHandler);
                            }
                        })
                );
            } else {
                res = rule.service(wxMessage, wxService, sessionManager, exceptionHandler);
                // 在同步操作结束，session访问结束
                log.debug("End session access: async=false, sessionId={}", wxMessage.getFromUser());
                sessionEndAccess(wxMessage);
            }
        }

        if (futures.size() > 0) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    for (Future future : futures) {
                        try {
                            future.get();
                            log.debug("End session access: async=true, sessionId={}", wxMessage.getFromUser());
                            // 异步操作结束，session访问结束
                            sessionEndAccess(wxMessage);
                        } catch (InterruptedException e) {
                            log.error("Error happened when wait task finish", e);
                        } catch (ExecutionException e) {
                            log.error("Error happened when wait task finish", e);
                        }
                    }
                }
            });
        }
        return res;
    }

    protected boolean isDuplicateMessage(WxXmlMessage wxMessage) {

        String messageId = "";
        if (wxMessage.getMsgId() == null) {
            messageId = String.valueOf(wxMessage.getCreateTime())
                    + "-" + wxMessage.getFromUser()
                    + "-" + String.valueOf(wxMessage.getEventKey() == null ? "" : wxMessage.getEventKey())
                    + "-" + String.valueOf(wxMessage.getEvent() == null ? "" : wxMessage.getEvent())
            ;
        } else {
            messageId = String.valueOf(wxMessage.getMsgId());
        }

        if (messageDuplicateChecker.isDuplicate(messageId)) {
            return true;
        }
        return false;
    }

    protected void sessionEndAccess(WxXmlMessage wxMessage) {
        InternalSession session = ((InternalSessionManager)sessionManager).findSession(wxMessage.getFromUser());
        if (session != null) {
            session.endAccess();
        }
    }

}
